Imports System.Configuration
Imports OpenArchive.System.Configuration
Imports OpenArchive.Document
Imports OpenArchive.Collections

Namespace OpenArchive
    Namespace System
        Public Class ArchiveSystem
            Public Const c_BinDirectoryName As String = "bin"
            Public Const c_StdLocation As String = "StdLocation"
            Public Const c_ArchivConfigSectionName As String = "Archivesystem"

            Private Shared m_synclock As Object
            Friend Shared m_FullBinDirectory As String
            Friend Shared m_ASUser As String
            Private m_Formatters As Generic.IList(Of Type)
            Private m_Components As Generic.IList(Of Type)
            Private m_dataProvider As Type
            Private WithEvents m_Fileeventwatcher As New Global.System.IO.FileSystemWatcher()
            Private Shared m_config As Global.System.Configuration.Configuration
            Private Shared m_RunningSystem As ArchiveSystem
            Private m_Archives As Archives
            Private m_WeakreferenceList As System.DB.WeakReferenceList
            Public Sub New()
                If m_synclock Is Nothing Then m_synclock = New Object
                SyncLock m_synclock
                    If m_RunningSystem Is Nothing Then
                        m_RunningSystem = Me
                        Init()
                    End If
                End SyncLock
            End Sub
            Private Sub Init()
                ' DoTo Load or create Settings
                If (Environment.OSVersion.Platform <> PlatformID.Win32NT) Then
                    Throw New PlatformNotSupportedException("RequiresWinNT")
                End If
                m_ASUser = Security.Principal.WindowsIdentity.GetCurrent.Name
                Dim _strAppDir As String = Global.System.IO.Path.GetDirectoryName(Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
                m_FullBinDirectory = Global.System.IO.Path.Combine(_strAppDir, c_BinDirectoryName) & Global.System.IO.Path.DirectorySeparatorChar
                '	Utils.EnsureAccessToApplicationBinDirectory()
                m_FullBinDirectory = New Global.System.Uri(m_FullBinDirectory).LocalPath

                PreloadAssembliesFromBin(New Global.System.IO.DirectoryInfo(m_FullBinDirectory))
                ' Loads

                InitDBProvider()

                ' Load Settings
                m_config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)


                If m_config.Sections(c_ArchivConfigSectionName) Is Nothing Then
                    Dim _archsection As ArchiveConfigurationSection = New ArchiveConfigurationSection()

                    m_config.Sections.Add(c_ArchivConfigSectionName, _archsection)
                    _archsection.Systemsettings.Add(New KeyValueConfigurationElement(c_StdLocation, Global.System.IO.Directory.GetDirectoryRoot(AppDomain.CurrentDomain.BaseDirectory) & "Archive"))

                    m_config.Save()
                    m_Archives = New Archives()
                Else
                    m_Archives = New Archives()
                End If


                Dim _myformatterlist As List(Of Type) = Formatters

                m_WeakreferenceList = New System.DB.WeakReferenceList()

                ' LoadSystemfiles


                'StartBinDirectoryMonitoring()




                ' Start Connections
            End Sub
            'Friend Sub AddSystemfile()
            '    Dim _archsyssection As ArchiveConfigurationSection
            '    Dim _dirsep As String = Global.System.IO.Path.DirectorySeparatorChar
            '    Archive.Location = Location & _dirsep & Archive.Name
            '    Try
            '        _archsyssection = System.ArchiveSystem.Settings.Sections(System.ArchiveSystem.c_ArchivConfigSectionName)
            '        If Not _archsyssection Is Nothing Then
            '            If _archsyssection.Archives.Item(Archive.Name) Is Nothing Then
            '                Dim _archivconfigelement As ArchiveConfigElement = New ArchiveConfigElement(Archive.Name, Archive.Location)
            '                _archsyssection.Archives.Add(_archivconfigelement)
            '            Else
            '                Throw New ArgumentException("Adding duplicate")
            '            End If
            '            System.ArchiveSystem.Settings.Save()
            '        Else
            '            Throw New Exception("System not initialized")
            '        End If
            '    Catch ex As Exception
            '        Throw ex
            '    End Try
            'End Sub
            Public Shared ReadOnly Property WeakReferenceList() As System.DB.WeakReferenceList
                Get
                    Return System.m_WeakreferenceList
                End Get
            End Property
            Private Sub LoadorInitSettings()

            End Sub
            Public Shared ReadOnly Property System() As ArchiveSystem
                Get
                    Return m_RunningSystem
                End Get
            End Property
            Public Shared Sub [Stop]()

            End Sub
            Public Shared Function IOCacheStream(ByVal LocationOrID As String) As Global.System.IO.Stream
                Return New Global.System.IO.FileStream(LocationOrID, Global.System.IO.FileMode.OpenOrCreate)
            End Function
            Public Shared Function Settings() As Global.System.Configuration.Configuration
                Return m_config
            End Function
            Public Function GetFormatter(ByVal Datacomponenttype As Type, ByVal Version As Version) As Formatters.IComponentFormatter
                For Each _formattertype As Type In Formatters
                    Dim _formatter As Formatters.IComponentFormatter = Activator.CreateInstance(_formattertype)
                    If _formatter.IsResponsible(Datacomponenttype, Version) Then Return _formatter
                Next
                Return Nothing
            End Function
            Public Function GetFormatter(ByVal Datacomponent As Components.Data.IComponentData) As Formatters.IComponentFormatter
                Return GetFormatter(Datacomponent.GetType, Datacomponent.Version)
            End Function
            Public Function GetComponent(ByVal Datacomponenttype As Type, ByVal Version As Version) As Components.IComponent
                Dim _paramarray(0) As Object
                _paramarray(0) = True


                For Each _componenttype As Type In Components
                    'Dim _component As Components.IComponent
                    '_component = Activator.CreateInstance(_componenttype, _paramarray)
                    Dim _component As Components.IComponent = Runtime.Serialization.FormatterServices.GetUninitializedObject(_componenttype)
                    If _component.IsResponsible(Datacomponenttype, Version) Then Return _component
                Next
                Return Nothing
            End Function
            Public Function GetComponent(ByVal Datacomponent As Components.Data.IComponentData) As Components.IComponent
                Return GetComponent(Datacomponent.GetType, Datacomponent.Version)
            End Function
            Public Shared ReadOnly Property DataProvider() As Type
                Get
                    Return m_RunningSystem.m_dataProvider
                End Get
            End Property
            Public ReadOnly Property Archives() As Archives
                Get
                    Return m_RunningSystem.m_Archives
                End Get
            End Property
            Private Sub InitDBProvider()
                If m_dataProvider Is Nothing Then
                    For Each _asm As Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
                        If Not _asm.GlobalAssemblyCache Then
                            For Each _typ As Type In _asm.GetTypes
                                If Utils.CheckInterfaceInType(_typ, GetType(DB.IArchiveDataProvider)) Then
                                    If Not _typ.IsAbstract Then
                                        m_dataProvider = _typ
                                        Exit Sub
                                    End If
                                End If
                            Next
                        End If
                    Next
                End If
            End Sub
            Private Sub PreloadAssembliesFromBin(ByVal dirInfo As Global.System.IO.DirectoryInfo)
                Dim info As Global.System.IO.FileInfo
                If Not dirInfo.Exists() Then Exit Sub
                For Each info In dirInfo.GetFiles("*.dll")
                    Try
                        Reflection.Assembly.Load(Utils.GetAssemblyNameFromFileName(info.Name))
                    Catch exception1 As Global.System.IO.FileNotFoundException
                        Try
                            Reflection.Assembly.LoadFrom(info.FullName)
                        Catch obj1 As Exception
                        End Try
                    Catch obj2 As Exception
                    End Try
                Next
                Dim info2 As Global.System.IO.DirectoryInfo
                For Each info2 In dirInfo.GetDirectories
                    PreloadAssembliesFromBin(info2)
                Next
            End Sub
            Private Sub StartBinDirectoryMonitoring()
                Try
                    m_Fileeventwatcher.Path = Global.System.IO.Path.GetDirectoryName(m_FullBinDirectory)    ' Checks also if the path is valid. If not exeption
                Catch ex As ArgumentException
                    Throw New Global.System.IO.DirectoryNotFoundException("Directory 'bin' not found", ex)
                End Try

                Try
                    m_Fileeventwatcher.Filter = "*.dll" ' Path.GetFileName(_Fileeventpath)	' Seperates the filename from the Filepath string and set it as a Filter for the event
                Catch ex As ArgumentException
                    Throw ex
                End Try
                ' Begin watching.
                m_Fileeventwatcher.NotifyFilter = (Global.System.IO.NotifyFilters.Size)
                m_Fileeventwatcher.EnableRaisingEvents = True
                m_Fileeventwatcher.IncludeSubdirectories = True
                AddHandler m_Fileeventwatcher.Changed, AddressOf DirectoryCriticalChanged
            End Sub

            Private Sub DirectoryCriticalChanged(ByVal source As Object, ByVal e As Global.System.IO.FileSystemEventArgs)
                m_Fileeventwatcher.Dispose()
                ' TO Do Something -> Reload or add assamblies
                PreloadAssembliesFromBin(New Global.System.IO.DirectoryInfo(m_FullBinDirectory))
                m_Fileeventwatcher = New Global.System.IO.FileSystemWatcher()
            End Sub
            Friend ReadOnly Property Formatters() As Generic.List(Of Type)
                Get
                    If m_Formatters Is Nothing Then
                        m_Formatters = New Generic.List(Of Type)
                        For Each _asm As Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
                            If Not _asm.GlobalAssemblyCache Then
                                For Each _typ As Type In _asm.GetTypes
                                    If Utils.CheckInterfaceInType(_typ, GetType(Formatters.IComponentFormatter)) Then ' , Formatters.IComponentFormatter) Then
                                        If Not _typ.IsAbstract Then
                                            m_Formatters.Add(_typ)
                                        End If
                                    End If
                                Next
                            End If
                        Next
                    End If
                    Return m_Formatters
                End Get
            End Property
            Friend ReadOnly Property Components() As Generic.List(Of Type)
                Get
                    If m_Components Is Nothing Then
                        m_Components = New Generic.List(Of Type)
                        For Each _asm As Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
                            If Not _asm.GlobalAssemblyCache Then
                                For Each _typ As Type In _asm.GetTypes
                                    If Utils.CheckInterfaceInType(_typ, GetType(Components.IComponent)) Then
                                        If Not _typ.IsAbstract Then
                                            m_Components.Add(_typ)
                                        End If
                                    End If
                                Next
                            End If
                        Next
                    End If
                    Return m_Components
                End Get
            End Property

            Friend Sub RegisterIndexType(ByVal IndexType As Type)
                If IsIndexTypeRegistered(IndexType) Then

                End If
            End Sub
            Private Function IsIndexTypeRegistered(ByVal IndexType As Type)

            End Function
            Friend Shared Function VerifyIndexType(ByVal Indextype As Type) As Boolean
                Dim _fieldinfos As Global.System.Reflection.FieldInfo() = Indextype.GetFields(Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public)
                For Each _fieldinfo As Reflection.FieldInfo In _fieldinfos
                    If _fieldinfo.FieldType.IsGenericType And _fieldinfo.FieldType.Name.StartsWith("IndexField") Then
                        Return True
                    End If
                Next
                Return False
            End Function
        End Class
    End Namespace

End Namespace

